 /*******************************************************************************
  * Copyright (c) 2000, 2004 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Common Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/cpl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.core.runtime.adaptor;

 import java.io.InputStream ;
 import java.util.*;
 import javax.xml.parsers.SAXParserFactory ;
 import org.eclipse.osgi.framework.adaptor.FrameworkAdaptor;
 import org.eclipse.osgi.framework.log.FrameworkLogEntry;
 import org.osgi.framework.BundleContext;
 import org.osgi.util.tracker.ServiceTracker;
 import org.xml.sax.*;
 import org.xml.sax.helpers.DefaultHandler ;

 /**
  * Internal class.
  */
 public class PluginParser extends DefaultHandler implements IModel {
     private static ServiceTracker xmlTracker = null;

     private PluginInfo manifestInfo = new PluginInfo();
     private BundleContext context;
     private String target; // The targeted platform for the given manifest

     public class PluginInfo implements IPluginInfo {
         private String schemaVersion;
         private String pluginId;
         private String version;
         private String vendor;

         // an ordered list of library path names.
 private ArrayList libraryPaths;
         // TODO Should get rid of the libraries map and just have a
 // list of library export statements instead. Library paths must
 // preserve order.
 private Map libraries; //represent the libraries and their export statement
 private ArrayList requires;
         private boolean requiresExpanded = false; //indicates if the requires have been processed.
 private boolean compatibilityFound = false; //set to true is the requirement list contain compatilibity
 private String pluginClass;
         private String masterPluginId;
         private String masterVersion;
         private String masterMatch;
         private Set filters;
         private String pluginName;
         private boolean singleton;
         private boolean fragment;
         private static final String TARGET21 = "2.1"; //$NON-NLS-1$

         public boolean isFragment() {
             return fragment;
         }

         public String toString() {
             return "plugin-id: " + pluginId + " version: " + version + " libraries: " + libraries + " class:" + pluginClass + " master: " + masterPluginId + " master-version: " + masterVersion + " requires: " + requires + " singleton: " + singleton; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$
 }

         public Map getLibraries() {
             if (libraries == null)
                 return new HashMap(0);
             return libraries;
         }

         public ArrayList getRequires() {
             if (!TARGET21.equals(target) && schemaVersion == null && !requiresExpanded) {
                 requiresExpanded = true;
                 if (requires == null) {
                     requires = new ArrayList(1);
                     requires.add(new Prerequisite(PluginConverterImpl.PI_RUNTIME, TARGET21, false, false, IModel.PLUGIN_REQUIRES_MATCH_GREATER_OR_EQUAL));
                     requires.add(new Prerequisite(PluginConverterImpl.PI_RUNTIME_COMPATIBILITY, null, false, false, null));
                 } else {
                     //Add elements on the requirement list of ui and help.
 for (int i = 0; i < requires.size(); i++) {
                         Prerequisite analyzed = (Prerequisite) requires.get(i);
                         if ("org.eclipse.ui".equals(analyzed.getName())) { //$NON-NLS-1$
 requires.add(i + 1, new Prerequisite("org.eclipse.ui.workbench.texteditor", null, true, analyzed.isExported(), null)); //$NON-NLS-1$
 requires.add(i + 1, new Prerequisite("org.eclipse.jface.text", null, true, analyzed.isExported(), null)); //$NON-NLS-1$
 requires.add(i + 1, new Prerequisite("org.eclipse.ui.editors", null, true, analyzed.isExported(), null)); //$NON-NLS-1$
 requires.add(i + 1, new Prerequisite("org.eclipse.ui.views", null, true, analyzed.isExported(), null)); //$NON-NLS-1$
 requires.add(i + 1, new Prerequisite("org.eclipse.ui.ide", null, true, analyzed.isExported(), null)); //$NON-NLS-1$
 } else if ("org.eclipse.help".equals(analyzed.getName())) { //$NON-NLS-1$
 requires.add(i + 1, new Prerequisite("org.eclipse.help.base", null, true, analyzed.isExported(), null)); //$NON-NLS-1$
 } else if (PluginConverterImpl.PI_RUNTIME.equals(analyzed.getName()) && !compatibilityFound) {
                             requires.add(i + 1, new Prerequisite(PluginConverterImpl.PI_RUNTIME_COMPATIBILITY, null, false, analyzed.isExported(), null));
                         }
                     }
                     if (!requires.contains(new Prerequisite(PluginConverterImpl.PI_RUNTIME_COMPATIBILITY, null, false, false, null))) {
                         requires.add(new Prerequisite(PluginConverterImpl.PI_RUNTIME_COMPATIBILITY, null, false, false, null));
                     }
                     //Remove any prereq on runtime and add a prereq on runtime 2.1
 //This is used to recognize the version for which the given plugin was initially targeted.
 Prerequisite runtimePrereq = new Prerequisite(PluginConverterImpl.PI_RUNTIME, null, false, false, null);
                     requires.remove(runtimePrereq);
                     requires.add(new Prerequisite(PluginConverterImpl.PI_RUNTIME, TARGET21, false, false, IModel.PLUGIN_REQUIRES_MATCH_GREATER_OR_EQUAL));
                 }
             }
             if (requires == null)
                 return requires = new ArrayList(0);

             return requires;
         }

         public String getMasterId() {
             return masterPluginId;
         }

         public String getMasterVersion() {
             return masterVersion;
         }

         public String getMasterMatch() {
             return masterMatch;
         }

         public String getPluginClass() {
             return pluginClass;
         }

         public String getUniqueId() {
             return pluginId;
         }

         public String getVersion() {
             return version;
         }

         public Set getPackageFilters() {
             return filters;
         }

         public String [] getLibrariesName() {
             if (libraryPaths == null)
                 return new String [0];
             return (String []) libraryPaths.toArray(new String [libraryPaths.size()]);
         }

         public String getPluginName() {
             return pluginName;
         }

         public String getProviderName() {
             return vendor;
         }

         public boolean isSingleton() {
             return singleton;
         }

         public String getRoot() {
             return isFragment() ? FRAGMENT : PLUGIN;
         }

         /*
          * Provides some basic form of validation. Since plugin/fragment is the only mandatory
          * attribute, it is the only one we cara about here.
          */
         public String validateForm() {
             if (this.pluginId == null)
                 return EclipseAdaptorMsg.formatter.getString("ECLIPSE_CONVERTER_MISSING_ATTRIBUTE", new String [] {getRoot(), PLUGIN_ID, getRoot()}); //$NON-NLS-1$
 if (this.pluginName == null)
                 return EclipseAdaptorMsg.formatter.getString("ECLIPSE_CONVERTER_MISSING_ATTRIBUTE", new String [] {getRoot(), PLUGIN_NAME, getRoot()}); //$NON-NLS-1$
 if (this.version == null)
                 return EclipseAdaptorMsg.formatter.getString("ECLIPSE_CONVERTER_MISSING_ATTRIBUTE", new String [] {getRoot(), PLUGIN_VERSION, getRoot()}); //$NON-NLS-1$
 if (isFragment() && this.masterPluginId == null)
                 return EclipseAdaptorMsg.formatter.getString("ECLIPSE_CONVERTER_MISSING_ATTRIBUTE", new String [] {getRoot(), FRAGMENT_PLUGIN_ID, getRoot()}); //$NON-NLS-1$
 if (isFragment() && this.masterVersion == null)
                 return EclipseAdaptorMsg.formatter.getString("ECLIPSE_CONVERTER_MISSING_ATTRIBUTE", new String [] {getRoot(), FRAGMENT_PLUGIN_VERSION, getRoot()}); //$NON-NLS-1$
 return null;
         }
     }

     // Current State Information
 Stack stateStack = new Stack();

     // Current object stack (used to hold the current object we are populating in this plugin info
 Stack objectStack = new Stack();
     Locator locator = null;

     // Valid States
 private static final int IGNORED_ELEMENT_STATE = 0;
     private static final int INITIAL_STATE = 1;
     private static final int PLUGIN_STATE = 2;
     private static final int PLUGIN_RUNTIME_STATE = 3;
     private static final int PLUGIN_REQUIRES_STATE = 4;
     private static final int PLUGIN_EXTENSION_POINT_STATE = 5;
     private static final int PLUGIN_EXTENSION_STATE = 6;
     private static final int RUNTIME_LIBRARY_STATE = 7;
     private static final int LIBRARY_EXPORT_STATE = 8;
     private static final int PLUGIN_REQUIRES_IMPORT_STATE = 9;
     private static final int FRAGMENT_STATE = 11;

     public PluginParser(BundleContext context, String target) {
         super();
         this.context = context;
         this.target = target;
     }

     /**
      * Receive a Locator object for document events.
      *
      * <p>
      * By default, do nothing. Application writers may override this method in
      * a subclass if they wish to store the locator for use with other document
      * events.
      * </p>
      *
      * @param locator A locator for all SAX document events.
      * @see org.xml.sax.ContentHandler#setDocumentLocator(org.xml.sax.Locator)
      * @see org.xml.sax.Locator
      */
     public void setDocumentLocator(Locator locator) {
         this.locator = locator;
     }

     public void endDocument() {
     }

     public void endElement(String uri, String elementName, String qName) {
         switch (((Integer ) stateStack.peek()).intValue()) {
             case IGNORED_ELEMENT_STATE :
                 stateStack.pop();
                 break;
             case INITIAL_STATE :
                 // shouldn't get here
 // internalError(Policy.bind("parse.internalStack", elementName)); //$NON-NLS-1$
 break;
             case PLUGIN_STATE :
             case FRAGMENT_STATE :
                 break;
             case PLUGIN_RUNTIME_STATE :
                 if (elementName.equals(RUNTIME)) {
                     stateStack.pop();
                 }
                 break;
             case PLUGIN_REQUIRES_STATE :
                 if (elementName.equals(PLUGIN_REQUIRES)) {
                     stateStack.pop();
                     objectStack.pop();
                 }
                 break;
             case PLUGIN_EXTENSION_POINT_STATE :
                 if (elementName.equals(EXTENSION_POINT)) {
                     stateStack.pop();
                 }
                 break;
             case PLUGIN_EXTENSION_STATE :
                 if (elementName.equals(EXTENSION)) {
                     stateStack.pop();
                 }
                 break;
             case RUNTIME_LIBRARY_STATE :
                 if (elementName.equals(LIBRARY)) {
                     String curLibrary = (String ) objectStack.pop();
                     if (!curLibrary.trim().equals("")) { //$NON-NLS-1$
 Vector exportsVector = (Vector) objectStack.pop();
                         if (manifestInfo.libraries == null) {
                             manifestInfo.libraries = new HashMap(3);
                             manifestInfo.libraryPaths = new ArrayList(3);
                         }
                         manifestInfo.libraries.put(curLibrary, exportsVector);
                         manifestInfo.libraryPaths.add(curLibrary.replace('\\', '/'));
                     }
                     stateStack.pop();
                 }
                 break;
             case LIBRARY_EXPORT_STATE :
                 if (elementName.equals(LIBRARY_EXPORT)) {
                     stateStack.pop();
                 }
                 break;
             case PLUGIN_REQUIRES_IMPORT_STATE :
                 if (elementName.equals(PLUGIN_REQUIRES_IMPORT)) {
                     stateStack.pop();
                 }
                 break;
         }
     }

     public void error(SAXParseException ex) {
         logStatus(ex);
     }

     public void fatalError(SAXParseException ex) throws SAXException {
         logStatus(ex);
         throw ex;
     }

     public void handleExtensionPointState(String elementName, Attributes attributes) {
         // nothing to do for extension-points' children
 stateStack.push(new Integer (IGNORED_ELEMENT_STATE));
     }

     public void handleExtensionState(String elementName, Attributes attributes) {
         // nothing to do for extensions' children
 stateStack.push(new Integer (IGNORED_ELEMENT_STATE));
     }

     public void handleInitialState(String elementName, Attributes attributes) {
         if (elementName.equals(PLUGIN)) {
             stateStack.push(new Integer (PLUGIN_STATE));
             parsePluginAttributes(attributes);
         } else if (elementName.equals(FRAGMENT)) {
             manifestInfo.fragment = true;
             stateStack.push(new Integer (FRAGMENT_STATE));
             parseFragmentAttributes(attributes);
         } else {
             stateStack.push(new Integer (IGNORED_ELEMENT_STATE));
             internalError(elementName);
         }
     }

     public void handleLibraryExportState(String elementName, Attributes attributes) {
         // All elements ignored.
 stateStack.push(new Integer (IGNORED_ELEMENT_STATE));
     }

     public void handleLibraryState(String elementName, Attributes attributes) {
         if (elementName.equals(LIBRARY_EXPORT)) {
             // Change State
 stateStack.push(new Integer (LIBRARY_EXPORT_STATE));
             // The top element on the stack much be a library element
 String currentLib = (String ) objectStack.peek();
             if (attributes == null)
                 return;
             String maskValue = attributes.getValue("", LIBRARY_EXPORT_MASK); //$NON-NLS-1$
 // pop off the library - already in currentLib
 objectStack.pop();
             Vector exportMask = (Vector) objectStack.peek();
             // push library back on
 objectStack.push(currentLib);
             //Split the export upfront
 if (maskValue != null) {
                 StringTokenizer tok = new StringTokenizer(maskValue, ","); //$NON-NLS-1$
 while (tok.hasMoreTokens()) {
                     String value = tok.nextToken();
                     if (!exportMask.contains(maskValue))
                         exportMask.addElement(value.trim());
                 }
             }
             return;
         }
         if (elementName.equals(LIBRARY_PACKAGES)) {
             stateStack.push(new Integer (IGNORED_ELEMENT_STATE));
             return;
         }
         stateStack.push(new Integer (IGNORED_ELEMENT_STATE));
         internalError(elementName);
         return;
     }

     public void handlePluginState(String elementName, Attributes attributes) {
         if (elementName.equals(RUNTIME)) {
             // We should only have one Runtime element in a plugin or fragment
 Object whatIsIt = objectStack.peek();
             if ((whatIsIt instanceof PluginInfo) && ((PluginInfo) objectStack.peek()).libraries != null) {
                 // This is at least the 2nd Runtime element we have hit. Ignore it.
 stateStack.push(new Integer (IGNORED_ELEMENT_STATE));
                 return;
             }
             stateStack.push(new Integer (PLUGIN_RUNTIME_STATE));
             // Push a new vector to hold all the library entries objectStack.push(new Vector());
 return;
         }
         if (elementName.equals(PLUGIN_REQUIRES)) {
             stateStack.push(new Integer (PLUGIN_REQUIRES_STATE));
             // Push a new vector to hold all the prerequisites
 objectStack.push(new Vector());
             parseRequiresAttributes(attributes);
             return;
         }
         if (elementName.equals(EXTENSION_POINT)) {
             // mark the plugin as singleton and ignore all elements under extension (if there are any)
 manifestInfo.singleton = true;
             stateStack.push(new Integer (PLUGIN_EXTENSION_POINT_STATE));
             return;
         }
         if (elementName.equals(EXTENSION)) {
             // mark the plugin as singleton and ignore all elements under extension (if there are any)
 manifestInfo.singleton = true;
             stateStack.push(new Integer (PLUGIN_EXTENSION_STATE));
             return;
         }
         // If we get to this point, the element name is one we don't currently accept.
 // Set the state to indicate that this element will be ignored
 stateStack.push(new Integer (IGNORED_ELEMENT_STATE));
         internalError(elementName);
     }

     public void handleRequiresImportState(String elementName, Attributes attributes) {
         // All elements ignored.
 stateStack.push(new Integer (IGNORED_ELEMENT_STATE));
     }

     public void handleRequiresState(String elementName, Attributes attributes) {
         if (elementName.equals(PLUGIN_REQUIRES_IMPORT)) {
             parsePluginRequiresImport(attributes);
             return;
         }
         // If we get to this point, the element name is one we don't currently accept.
 // Set the state to indicate that this element will be ignored
 stateStack.push(new Integer (IGNORED_ELEMENT_STATE));
         internalError(elementName);
     }

     public void handleRuntimeState(String elementName, Attributes attributes) {
         if (elementName.equals(LIBRARY)) {
             // Change State
 stateStack.push(new Integer (RUNTIME_LIBRARY_STATE));
             // Process library attributes
 parseLibraryAttributes(attributes);
             return;
         }
         // If we get to this point, the element name is one we don't currently accept.
 // Set the state to indicate that this element will be ignored
 stateStack.push(new Integer (IGNORED_ELEMENT_STATE));
         internalError(elementName);
     }

     private void logStatus(SAXParseException ex) {
         String name = ex.getSystemId();
         if (name == null)
             name = ""; //$NON-NLS-1$
 else
             name = name.substring(1 + name.lastIndexOf("/")); //$NON-NLS-1$
 String msg;
         if (name.equals("")) //$NON-NLS-1$
 msg = EclipseAdaptorMsg.formatter.getString("parse.error", ex.getMessage()); //$NON-NLS-1$
 else
             msg = EclipseAdaptorMsg.formatter.getString("parse.errorNameLineColumn", new String [] {name, Integer.toString(ex.getLineNumber()), Integer.toString(ex.getColumnNumber()), ex.getMessage()}); //$NON-NLS-1$

         FrameworkLogEntry entry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, msg, 0, ex, null);
         EclipseAdaptor.getDefault().getFrameworkLog().log(entry);
     }

     synchronized public PluginInfo parsePlugin(InputStream in) throws Exception {
         SAXParserFactory factory = acquireXMLParsing(context);
         if (factory == null) {
             FrameworkLogEntry entry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, EclipseAdaptorMsg.formatter.getString("ECLIPSE_CONVERTER_NO_SAX_FACTORY"), 0, null, null); //$NON-NLS-1$
 EclipseAdaptor.getDefault().getFrameworkLog().log(entry);
             return null;
         }

         factory.setNamespaceAware(true);
         factory.setNamespaceAware(true);
         try {
             factory.setFeature("http://xml.org/sax/features/string-interning", true); //$NON-NLS-1$
 } catch (SAXException se) {
             // ignore; we can still operate without string-interning
 }
         factory.setValidating(false);
         factory.newSAXParser().parse(in, this);
         return manifestInfo;
     }

     public static SAXParserFactory acquireXMLParsing(BundleContext context) {
         if (xmlTracker == null) {
             xmlTracker = new ServiceTracker(context, "javax.xml.parsers.SAXParserFactory", null);
             xmlTracker.open();
         }
         return (SAXParserFactory ) xmlTracker.getService();
     }

     public static void releaseXMLParsing() {
         if (xmlTracker != null)
             xmlTracker.close();
     }

     public void parseFragmentAttributes(Attributes attributes) {
         // process attributes
 objectStack.push(manifestInfo);
         int len = attributes.getLength();
         for (int i = 0; i < len; i++) {
             String attrName = attributes.getLocalName(i);
             String attrValue = attributes.getValue(i).trim();
             if (attrName.equals(FRAGMENT_ID))
                 manifestInfo.pluginId = attrValue;
             else if (attrName.equals(FRAGMENT_NAME))
                 manifestInfo.pluginName = attrValue;
             else if (attrName.equals(FRAGMENT_VERSION))
                 manifestInfo.version = attrValue;
             else if (attrName.equals(FRAGMENT_PROVIDER))
                 manifestInfo.vendor = attrValue;
             else if (attrName.equals(FRAGMENT_PLUGIN_ID))
                 manifestInfo.masterPluginId = attrValue;
             else if (attrName.equals(FRAGMENT_PLUGIN_VERSION))
                 manifestInfo.masterVersion = attrValue;
             else if (attrName.equals(FRAGMENT_PLUGIN_MATCH))
                 manifestInfo.masterMatch = attrValue;
         }
     }

     public void parseLibraryAttributes(Attributes attributes) {
         // Push a vector to hold the export mask
 objectStack.push(new Vector());
         String current = attributes.getValue("", LIBRARY_NAME); //$NON-NLS-1$
 objectStack.push(current);
     }

     public void parsePluginAttributes(Attributes attributes) {
         // process attributes
 objectStack.push(manifestInfo);
         int len = attributes.getLength();
         for (int i = 0; i < len; i++) {
             String attrName = attributes.getLocalName(i);
             String attrValue = attributes.getValue(i).trim();
             if (attrName.equals(PLUGIN_ID))
                 manifestInfo.pluginId = attrValue;
             else if (attrName.equals(PLUGIN_NAME))
                 manifestInfo.pluginName = attrValue;
             else if (attrName.equals(PLUGIN_VERSION))
                 manifestInfo.version = attrValue;
             else if (attrName.equals(PLUGIN_VENDOR) || (attrName.equals(PLUGIN_PROVIDER)))
                 manifestInfo.vendor = attrValue;
             else if (attrName.equals(PLUGIN_CLASS))
                 manifestInfo.pluginClass = attrValue;
         }
     }

     public class Prerequisite {
         String name;
         String version;
         boolean optional;
         boolean export;
         String match;

         public boolean isExported() {
             return export;
         }

         public String getMatch() {
             return match;
         }

         public String getName() {
             return name;
         }

         public boolean isOptional() {
             return optional;
         }

         public String getVersion() {
             return version;
         }

         public Prerequisite(String preqName, String prereqVersion, boolean isOtional, boolean isExported, String prereqMatch) {
             name = preqName;
             version = prereqVersion;
             optional = isOtional;
             export = isExported;
             match = prereqMatch;
         }

         public String toString() {
             return name;
         }

         public boolean equals(Object prereq) {
             if (!(prereq instanceof Prerequisite))
                 return false;
             return name.equals(((Prerequisite) prereq).name);
         }
     }

     public void parsePluginRequiresImport(Attributes attributes) {
         if (manifestInfo.requires == null) {
             manifestInfo.requires = new ArrayList();
             // to avoid cycles
 // if (!manifestInfo.pluginId.equals(PluginConverterImpl.PI_RUNTIME)) //$NON-NLS-1$
 // manifestInfo.requires.add(new Prerequisite(PluginConverterImpl.PI_RUNTIME, null, false, false, null)); //$NON-NLS-1$
 }
         // process attributes
 String plugin = attributes.getValue("", PLUGIN_REQUIRES_PLUGIN); //$NON-NLS-1$
 if (plugin == null)
             return;
         if (plugin.equals(PluginConverterImpl.PI_BOOT)) //$NON-NLS-1$//$NON-NLS-2$
 return;
         if (plugin.equals(PluginConverterImpl.PI_RUNTIME_COMPATIBILITY))
             manifestInfo.compatibilityFound = true;
         String version = attributes.getValue("", PLUGIN_REQUIRES_PLUGIN_VERSION); //$NON-NLS-1$
 String optional = attributes.getValue("", PLUGIN_REQUIRES_OPTIONAL); //$NON-NLS-1$
 String export = attributes.getValue("", PLUGIN_REQUIRES_EXPORT); //$NON-NLS-1$
 String match = attributes.getValue("", PLUGIN_REQUIRES_MATCH); //$NON-NLS-1$
 manifestInfo.requires.add(new Prerequisite(plugin, version, "true".equalsIgnoreCase(optional) ? true : false, "true".equalsIgnoreCase(export) ? true : false, match)); //$NON-NLS-1$ //$NON-NLS-2$
 }

     public void parseRequiresAttributes(Attributes attributes) {
         //Nothing to do.
 }

     static String replace(String s, String from, String to) {
         String str = s;
         int fromLen = from.length();
         int toLen = to.length();
         int ix = str.indexOf(from);
         while (ix != -1) {
             str = str.substring(0, ix) + to + str.substring(ix + fromLen);
             ix = str.indexOf(from, ix + toLen);
         }
         return str;
     }

     public void startDocument() {
         stateStack.push(new Integer (INITIAL_STATE));
     }

     public void startElement(String uri, String elementName, String qName, Attributes attributes) {
         switch (((Integer ) stateStack.peek()).intValue()) {
             case INITIAL_STATE :
                 handleInitialState(elementName, attributes);
                 break;
             case FRAGMENT_STATE :
             case PLUGIN_STATE :
                 handlePluginState(elementName, attributes);
                 break;
             case PLUGIN_RUNTIME_STATE :
                 handleRuntimeState(elementName, attributes);
                 break;
             case PLUGIN_REQUIRES_STATE :
                 handleRequiresState(elementName, attributes);
                 break;
             case PLUGIN_EXTENSION_POINT_STATE :
                 handleExtensionPointState(elementName, attributes);
                 break;
             case PLUGIN_EXTENSION_STATE :
                 handleExtensionState(elementName, attributes);
                 break;
             case RUNTIME_LIBRARY_STATE :
                 handleLibraryState(elementName, attributes);
                 break;
             case LIBRARY_EXPORT_STATE :
                 handleLibraryExportState(elementName, attributes);
                 break;
             case PLUGIN_REQUIRES_IMPORT_STATE :
                 handleRequiresImportState(elementName, attributes);
                 break;
             default :
                 stateStack.push(new Integer (IGNORED_ELEMENT_STATE));
         }
     }

     public void warning(SAXParseException ex) {
         logStatus(ex);
     }

     private void internalError(String elementName) {
         FrameworkLogEntry error;
         String message = EclipseAdaptorMsg.formatter.getString("ECLIPSE_CONVERTER_PARSE_UNKNOWNTOP_ELEMENT", elementName); //$NON-NLS-1$
 error = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, (manifestInfo.pluginId == null ? message : "Plug-in : " + manifestInfo.pluginId + ", " + message), 0, null, null); //$NON-NLS-1$ //$NON-NLS-2$
 EclipseAdaptor.getDefault().getFrameworkLog().log(error);
     }

     public void processingInstruction(String target, String data) throws SAXException {
         // Since 3.0, a processing instruction of the form <?eclipse version="3.0"?> at
 // the start of the manifest file is used to indicate the plug-in manifest
 // schema version in effect. Pre-3.0 (i.e., 2.1) plug-in manifest files do not
 // have one of these, and this is how we can distinguish the manifest of a
 // pre-3.0 plug-in from a post-3.0 one (for compatibility tranformations).
 if (target.equalsIgnoreCase("eclipse")) { //$NON-NLS-1$
 // just the presence of this processing instruction indicates that this
 // plug-in is at least 3.0
 manifestInfo.schemaVersion = "3.0"; //$NON-NLS-1$
 StringTokenizer tokenizer = new StringTokenizer(data, "=\""); //$NON-NLS-1$
 while (tokenizer.hasMoreTokens()) {
                 String token = tokenizer.nextToken();
                 if (token.equalsIgnoreCase("version")) { //$NON-NLS-1$
 if (!tokenizer.hasMoreTokens()) {
                         break;
                     }
                     manifestInfo.schemaVersion = tokenizer.nextToken();
                     break;
                 }
             }
         }
     }
 }
